voicemeeter\interface\callback/
register.rs1use std::{
3 ffi::{CString, NulError},
4 os::raw::c_long,
5 ptr,
6};
7
8use crate::{
9 bindings::VBVMR_CBCOMMAND, interface::callback::data::RawCallbackData, CallbackCommand,
10 VoicemeeterRemote,
11};
12
13fn register_audio_callback<'cb, F>(
26 remote: &VoicemeeterRemote,
27 mode: &crate::AudioCallbackMode,
28 application: *mut std::os::raw::c_char,
29 callback: F,
30) -> Result<*mut F, AudioCallbackRegisterError>
31where
32 F: FnMut(CallbackCommand<'cb>, i32) -> c_long,
33{
34 let data = Box::into_raw(Box::new(callback));
36 tracing::debug!("callback {:p}", data);
37 let res = unsafe {
38 remote.raw.VBVMR_AudioCallbackRegister(
39 mode.0,
40 Some(call_closure::<F>),
41 data as *mut _,
42 application,
43 )
44 };
45 tracing::debug!("registered application");
46 match res {
47 0 => Ok(data),
48 -1 => Err(AudioCallbackRegisterError::NoServer),
49 1 => Err(AudioCallbackRegisterError::AlreadyRegistered(unsafe {
50 CString::from_raw(application)
51 })),
52 s => Err(AudioCallbackRegisterError::Unexpected(s)),
53 }
54}
55
56unsafe extern "C" fn call_closure<'cb, F>(
57 user_data: *mut std::os::raw::c_void,
58 command: c_long,
59 buffer: *mut std::os::raw::c_void,
60 nnn: c_long,
61) -> c_long
62where
63 F: FnMut(CallbackCommand<'cb>, i32) -> c_long,
64{
65 let callback_ptr = user_data as *mut F;
66 let callback = unsafe { &mut *callback_ptr };
67 let ptr = RawCallbackData::from_ptr(buffer);
68 callback(
69 unsafe {
70 CallbackCommand::new_unchecked(
71 crate::types::VoicemeeterApplication::PotatoX64Bits,
72 VBVMR_CBCOMMAND(command),
73 ptr,
74 )
75 },
76 nnn,
77 )
78}
79
80#[must_use = "This structure contains the raw pointer to the closure environment, if this is not returned you will leak memory"]
82pub struct CallbackGuard<'a, F> {
83 guard: *mut F,
84 lt: std::marker::PhantomData<&'a ()>,
85}
86
87impl VoicemeeterRemote {
88 #[doc = include_str!("../../../examples/simple.rs")]
100 #[doc = include_str!("../../../examples/output.rs")]
105 #[tracing::instrument(skip(application_name, callback), fields(application_name, mode))]
108 pub fn audio_callback_register<'a, 'cb, 'g, F>(
109 &'a self,
110 mode: crate::AudioCallbackMode,
111 application_name: impl AsRef<str>,
112 callback: F,
113 ) -> Result<CallbackGuard<'g, F>, AudioCallbackRegisterError>
114 where
115 F: FnMut(CallbackCommand<'cb>, i32) -> c_long + 'g,
116 {
117 let application_name = application_name.as_ref();
118 tracing::Span::current().record("application_name", application_name);
119 assert!(application_name.len() < 64);
122 let mut application = [b'\0'; 64];
123 application[0..application_name.len()].copy_from_slice(application_name.as_bytes());
124 let ptr = ptr::addr_of!(self.program);
125 tracing::info!("a: {ptr:p}");
126
127 let g = register_audio_callback(
128 self,
129 &mode,
130 ptr::addr_of_mut!(application) as *mut _,
131 callback,
132 )?;
133
134 Ok(CallbackGuard {
135 guard: g,
136 lt: Default::default(),
137 })
138 }
139
140 pub fn audio_callback_unregister<F>(
142 &self,
143 guard: CallbackGuard<'_, F>,
144 ) -> Result<(), AudioCallbackUnregisterError> {
145 let res = unsafe { self.raw.VBVMR_AudioCallbackUnregister() };
146 match res {
147 0 => {
148 let _ = unsafe { Box::from_raw(guard.guard) };
149 Ok(())
150 }
151 -1 => Err(AudioCallbackUnregisterError::NoServer),
152 1 => Err(AudioCallbackUnregisterError::AlreadyUnregistered),
153 s => Err(AudioCallbackUnregisterError::Unexpected(s)),
154 }
155 }
156
157 pub fn audio_callback_unregister_leak<F>(&self) -> Result<(), AudioCallbackUnregisterError> {
159 let res = unsafe { self.raw.VBVMR_AudioCallbackUnregister() };
160 match res {
161 0 => Ok(()),
162 -1 => Err(AudioCallbackUnregisterError::NoServer),
163 1 => Err(AudioCallbackUnregisterError::AlreadyUnregistered),
164 s => Err(AudioCallbackUnregisterError::Unexpected(s)),
165 }
166 }
167}
168
169#[derive(Debug, Clone, thiserror::Error)]
171#[non_exhaustive]
172pub enum AudioCallbackRegisterError {
173 #[error("no server")]
176 NoServer,
177 #[error("an application `{}` is already registered", _0.to_string_lossy())]
179 AlreadyRegistered(CString),
180 #[error("could not make application name into a c-string")]
182 NulError(#[from] NulError),
183 #[error("unexpected error occurred: error code {0}")]
185 Unexpected(i32),
186}
187
188#[derive(Debug, Clone, thiserror::Error)]
190#[non_exhaustive]
191pub enum AudioCallbackUnregisterError {
192 #[error("no server")]
194 NoServer,
195 #[error("callback already unregistered")]
197 AlreadyUnregistered,
198 #[error("an unexpected error occurred: error code {0}")]
200 Unexpected(i32),
201}